#pragma once
#ifndef __GLF_DEBUGGER_PACKET_H__
#define __GLF_DEBUGGER_PACKET_H__

#include <glf/core/types.h>
#include <glf/debugger/debuggerAllocator.h>
#include <vector>
#include <string.h>

namespace glf
{
namespace debugger
{

class CNetSocket;
class Debugger;
class Module;

class PacketWriter
{
public:

	PacketWriter()
		: mSize(0)
	{
	}

	void Reserve(uint bytes)
	{
		mData.reserve(bytes);
	}

	PacketWriter& Init(uint type)
	{
		mType = type;
		mSize = 0;
		return *this;
	}

	PacketWriter& Write(const void* d, uint len)
	{
		if(mSize + len > mData.size())
		{
			mData.resize(mSize + len);
		}

		const uchar* ptr = (const uchar*)d;
		memcpy(&mData[0] + mSize, ptr, len);
		mSize += len;
		return *this;
	}

	PacketWriter& Write(const char* s)
	{
		Write(s, strlen(s));
		Write("\0", 1);
		return *this;
	}

	PacketWriter& Write(uint64 i)
	{
		Write((uchar)(i & 0xff));
		Write((uchar)((i >> 8) & 0xff));
		Write((uchar)((i >> 16) & 0xff));
		Write((uchar)((i >> 24) & 0xff));

		Write((uchar)((i >> 32) & 0xff));
		Write((uchar)((i >> 40) & 0xff));
		Write((uchar)((i >> 48) & 0xff));
		return Write((uchar)((i >> 56) & 0xff));
	}

	PacketWriter& Write(uint i)
	{
		Write((uchar)(i & 0xff));
		Write((uchar)((i >> 8) & 0xff));
		Write((uchar)((i >> 16) & 0xff));
		return Write((uchar)((i >> 24) & 0xff));
	}

	PacketWriter& Write(int i)
	{
		Write((uchar)(i & 0xff));
		Write((uchar)((i >> 8) & 0xff));
		Write((uchar)((i >> 16) & 0xff));
		return Write((uchar)((i >> 24) & 0xff));
	}

	PacketWriter& Write(ulong i)
	{
		if(sizeof(ulong) == 4) {
			return Write((uint)i);
		}
		return Write((uint64)i);
	}

	PacketWriter& Write(long i)
	{
		if(sizeof(long) == 4) {
			return Write((int)i);
		}
		return Write((uint64)i);
	}

	PacketWriter& Write(float i)
	{
		uint u = *(uint*)&i;
		Write((uchar)(u & 0xff));
		Write((uchar)((u >> 8) & 0xff));
		Write((uchar)((u >> 16) & 0xff));
		return Write((uchar)((u >> 24) & 0xff));
	}

	PacketWriter& Write(ushort i)
	{
		Write((uchar)(i & 0xff));
		return Write((uchar)((i >> 8) & 0xff));
	}

	PacketWriter& Write(short i)
	{
		Write((uchar)(i & 0xff));
		return Write((uchar)((i >> 8) & 0xff));
	}

	PacketWriter& Write(uchar i)
	{
		return Write(&i, sizeof(uchar));
	}

	PacketWriter& Write(bool i)
	{
		uchar value = i ? 1 : 0;
		return Write(&value, sizeof(uchar));
	}

	uint Size() const
	{
		return mSize;
	}

	void Flush(Module* module, CNetSocket* s);

private:

	std::vector<uchar, DebuggerAllocator<uchar> > mData;
	uint mSize;
	uint mType;

	friend class Debugger;
};

class PacketReader
{
public:

	PacketReader(const uchar* data, uint len)
		: mData(data)
		, mEnd(data + len)
	{
	}

	uint Available()
	{
		return mEnd - mData;
	}

	uint Read(uchar* buffer, uint size)
	{
		GLF_ASSERT(Available() >= size);
		memcpy(buffer, mData, size);
		mData += size;
		return size;
	}

	uchar ReadByte()
	{
		GLF_ASSERT(mData + 1 <= mEnd);

		uchar value = *(mData++);
		return value;
	}

	int ReadIntLE()
	{
		GLF_ASSERT(mData + 4 <= mEnd);

		int value = 0;
		value |= *(mData++);
		value |= *(mData++) << 8;
		value |= *(mData++) << 16;
		value |= *(mData++) << 24;
		return value;
	}

	int64 ReadLongLE()
	{
		GLF_ASSERT(mData + 4 <= mEnd);

		uint64 value = 0;
		value |= *(mData++);
		value |= *(mData++) << 8;
		value |= *(mData++) << 16;
		value |= *(mData++) << 24;
		
		value |= (uint64)*(mData++) << 32;
		value |= (uint64)*(mData++) << 40;
		value |= (uint64)*(mData++) << 48;
		value |= (uint64)*(mData++) << 56;
		return (int64)value;
	}

	float ReadFloatLE()
	{
		int value = ReadIntLE();
		return *(float*)&value;
	}

	stringdbg ReadString()
	{
		stringdbg str;
		while(mData < mEnd)
		{
			char c = *(mData++);
			if(c == 0)
				break;
			str += c;
		}
		return str;
	}

	const uchar* GetData()
	{
		return mData;
	}

private:

	const uchar* mData;
	const uchar* mEnd;
};

} // end namespace debugger
} // end namespace glf

#endif //__GLF_DEBUGGER_PACKET_H__
